package com.eurobcoin.controller;

import java.util.List;
import java.util.Locale;

import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.eurobcoin.balance.UserBalanceManager;
import com.eurobcoin.balance.UserBalanceModel;
import com.eurobcoin.helper.NumberHelper;
import com.eurobcoin.manager.DAOManager;
import com.eurobcoin.model.IndexPageModel;
import com.eurobcoin.trading.OrderAuditManager;
import com.eurobcoin.trading.TradingManager;
import com.eurobcoin.trading.TradingOperationResult;
import com.eurobcoin.trading.TradingOrder;
import com.eurobcoin.trading.TradingPair;
import com.eurobcoin.user.UserSession;

@Controller
@RequestMapping("/")
public class MainController {

	private static final String REDIRECT_FILIPIDES_INDEX = "redirect:/filipides/index.html";
	private static final String REDIRECT_LOGIN = "redirect:/login.html";
	private static final String REDIRECT_INDEX = "redirect:/index.html";
	
	private static final String INDEX_VIEW = "index";
	
	private static final Logger logger = Logger.getLogger(MainController.class);
	
	@Autowired
	private UserSession user;
	
	@Autowired
	private UserBalanceManager userBalanceManager;
	
	@Autowired
	private DAOManager daoManager;
	
	@Autowired
	private TradingManager tradingManager;
	
	@Autowired
	private OrderAuditManager orderAuditManager;
	
	@ModelAttribute("pairs")
	public List<TradingPair> getTradingPairs() {
		
		List<TradingPair> ret = null;
		
		ret = this.tradingManager.getTradingPairs();
		
		if(ret == null) {
			
			SqlSession sqlSession = this.daoManager.openSession();
			
			ret = this.tradingManager.getTradingPairs(sqlSession);
			
			sqlSession.close();
			
		}
		
		return ret;
	}
	
	@ModelAttribute("user")
	public UserSession getUserSession() {
		return this.user;
	}
	
	@ModelAttribute("userBalance")
	public List<UserBalanceModel> getUserBalanceModel() {
		List<UserBalanceModel> ret = null;
		
		if(this.user.isAuthenticated()) {
			
			SqlSession sqlSession = this.daoManager.openSession();
			
			ret = this.userBalanceManager.getUserBalanceById(
					sqlSession, 
					this.user.getUserModel().getId(), 
					new Locale(this.user.getUserModel().getLocale()));
			
			sqlSession.close();
		}
		
		return ret;
	}
	
	@RequestMapping(value="/buy.html", method=RequestMethod.POST)
	public ModelAndView buy(
			@RequestParam(value="buy_amount") String buyAmount,
			@RequestParam(value="buy_price") String buyPrice) throws Exception {
		
		StringBuilder trace = new StringBuilder("BUY ORDER - [");
		trace.append("userId=");
		trace.append(this.user.getUserModel().getId());
		trace.append(";");
		trace.append("username=");
		trace.append(this.user.getUserModel().getName());
		trace.append(";");
		trace.append("buyAmount=");
		trace.append(buyAmount);
		trace.append(";");
		trace.append("buyPrice=");
		trace.append(buyPrice);
		trace.append(";");
		
		ModelAndView ret = new ModelAndView(REDIRECT_INDEX);
		
		SqlSession sqlSession = this.daoManager.openSession();
		
		TradingPair tradingPair = this.tradingManager.getTradingPairById(
				sqlSession, 
				this.user.getTradingPair());
		
		long longBuyAmount = NumberHelper.stringToLong(
				buyAmount, 
				tradingPair.getFirstCurrencyDecimals(),
				new Locale(this.user.getUserModel().getLocale()));
		
		long longBuyPrice = NumberHelper.stringToLong(
				buyPrice, 
				tradingPair.getSecondCurrencyDecimals(),
				new Locale(this.user.getUserModel().getLocale()));
		
		UserBalanceModel userFirstCurrencyBalance = this.userBalanceManager.getUserBalanceByCurrencyId(
				sqlSession, 
				this.user.getUserModel().getId(), 
				tradingPair.getFirstCurrencyId(), 
				new Locale(this.user.getUserModel().getLocale()));
		
		UserBalanceModel userSecondCurrencyBalance = this.userBalanceManager.getUserBalanceByCurrencyId(
				sqlSession, 
				this.user.getUserModel().getId(), 
				tradingPair.getSecondCurrencyId(), 
				new Locale(this.user.getUserModel().getLocale()));
		
		long factor = (long)Math.pow(10.0, (double)tradingPair.getFirstCurrencyDecimals());
		long currencyAmountNeeded = (long)((double)longBuyAmount * (double)longBuyPrice / (double)factor);
		
		if(currencyAmountNeeded > userSecondCurrencyBalance.getBalance()) {
			
			// not enough funds
			
		} else {
			
			// audit order
			this.orderAuditManager.auditOrder(
					sqlSession, 
					"BUY", 
					(int)longBuyAmount, 
					(int)longBuyPrice, 
					tradingPair.getFirstCurrencyCode(), 
					tradingPair.getSecondCurrencyCode(), 
					this.user.getUserModel().getName());
			
			// process order
			TradingOperationResult tradingOperationResult = this.tradingManager.buyOrder(
					sqlSession, 
					this.user.getUserModel().getId(), 
					this.user.getUserModel().getName(),
					tradingPair,
					longBuyAmount, 
					longBuyPrice);
			
			// update balance
			long newFirstCurrencyBalance = userFirstCurrencyBalance.getBalance();
			long newSecondCurrencyBalance = userSecondCurrencyBalance.getBalance();
			
			
		}
		
		sqlSession.commit();
		sqlSession.close();
		
		return ret;
		
	}
	
	@RequestMapping(value="/sell.html", method=RequestMethod.POST)
	public ModelAndView sell(
			@RequestParam(value="sell_amount") String sellAmount,
			@RequestParam(value="sell_price") String sellPrice) throws Exception {
		
		StringBuilder trace = new StringBuilder("BUY ORDER - [");
		trace.append("userId=");
		trace.append(this.user.getUserModel().getId());
		trace.append(";");
		trace.append("username=");
		trace.append(this.user.getUserModel().getName());
		trace.append(";");
		trace.append("sellAmount=");
		trace.append(sellAmount);
		trace.append(";");
		trace.append("sellPrice=");
		trace.append(sellPrice);
		trace.append(";");
		
		ModelAndView ret = new ModelAndView(REDIRECT_INDEX);
		
		SqlSession sqlSession = this.daoManager.openSession();
		
		TradingPair tradingPair = this.tradingManager.getTradingPairById(
				sqlSession, 
				this.user.getTradingPair());
		
		long longSellAmount = NumberHelper.stringToLong(
				sellAmount, 
				tradingPair.getFirstCurrencyDecimals(),
				new Locale(this.user.getUserModel().getLocale()));
		
		long longSellPrice = NumberHelper.stringToLong(
				sellPrice, 
				tradingPair.getSecondCurrencyDecimals(),
				new Locale(this.user.getUserModel().getLocale()));
		
		UserBalanceModel userFirstCurrencyBalance = this.userBalanceManager.getUserBalanceByCurrencyId(
				sqlSession, 
				this.user.getUserModel().getId(), 
				tradingPair.getFirstCurrencyId(), 
				new Locale(this.user.getUserModel().getLocale()));
		
		UserBalanceModel userSecondCurrencyBalance = this.userBalanceManager.getUserBalanceByCurrencyId(
				sqlSession, 
				this.user.getUserModel().getId(), 
				tradingPair.getSecondCurrencyId(), 
				new Locale(this.user.getUserModel().getLocale()));
		
		long factor = (long)Math.pow(10.0, (double)tradingPair.getFirstCurrencyDecimals());
		long currencyAmountNeeded = (long)((double)longSellAmount * (double)longSellPrice / (double)factor);
		
		if(currencyAmountNeeded > userSecondCurrencyBalance.getBalance()) {
			
			// not enough funds
			
		} else {
			
			// audit order
			this.orderAuditManager.auditOrder(
					sqlSession, 
					"SELL", 
					(int)longSellAmount, 
					(int)longSellPrice, 
					tradingPair.getFirstCurrencyCode(), 
					tradingPair.getSecondCurrencyCode(), 
					this.user.getUserModel().getName());
			
			// process order
			TradingOperationResult tradingOperationResult = this.tradingManager.sellOrder(
					sqlSession, 
					this.user.getUserModel().getId(), 
					this.user.getUserModel().getName(),
					tradingPair,
					longSellAmount, 
					longSellPrice);
			
			// update balance
			long newFirstCurrencyBalance = userFirstCurrencyBalance.getBalance();
			long newSecondCurrencyBalance = userSecondCurrencyBalance.getBalance();
			
			
		}
		
		sqlSession.commit();
		sqlSession.close();
		
		return ret;
		
	}
	
	@RequestMapping(value="/index.html", method=RequestMethod.GET)
	public ModelAndView doIndex() throws Exception {

		ModelAndView ret = null;
		
		if(this.user.getUserModel().getId() == 0) {
			ret = new ModelAndView(REDIRECT_LOGIN);
		} else {
			
			SqlSession sqlSession = this.daoManager.openSession();
			
			ret = new ModelAndView(INDEX_VIEW);
			
			IndexPageModel indexPageModel = new IndexPageModel();
			
			TradingPair tradingPair = this.tradingManager.getTradingPairById(sqlSession, this.user.getTradingPair());
			
			indexPageModel.setLeftCurrencyId(tradingPair.getFirstCurrencyId());
			indexPageModel.setRightCurrencyId(tradingPair.getSecondCurrencyId());
			
			indexPageModel.setLeftCurrencyCode(tradingPair.getFirstCurrencyCode());
			indexPageModel.setRightCurrencyCode(tradingPair.getSecondCurrencyCode());
			
			UserBalanceModel leftBalance = this.userBalanceManager.getUserBalanceByCurrencyId(
					sqlSession, 
					this.user.getUserModel().getId(), 
					tradingPair.getFirstCurrencyId(), 
					new Locale(this.user.getUserModel().getLocale()));
			
			UserBalanceModel rightBalance = this.userBalanceManager.getUserBalanceByCurrencyId(
					sqlSession, 
					this.user.getUserModel().getId(), 
					tradingPair.getSecondCurrencyId(), 
					new Locale(this.user.getUserModel().getLocale()));
			
			indexPageModel.setLeftCurrencyAmount(leftBalance.getBalance());
			indexPageModel.setRightCurrencyAmount(rightBalance.getBalance());
			
			indexPageModel.setLeftCurrencyDecimals(leftBalance.getDecimals());
			indexPageModel.setRightCurrencyDecimals(rightBalance.getDecimals());
			
			indexPageModel.setLocale(new Locale(this.user.getUserModel().getLocale()));
			
			List<TradingOrder> buyOrders = tradingManager.getBuyOrders(
					sqlSession, 
					leftBalance.getCurrencyId(), 
					rightBalance.getCurrencyId(),
					leftBalance.getDecimals(),
					rightBalance.getDecimals(),
					new Locale(this.user.getUserModel().getLocale()));
			
			indexPageModel.setBuyOrders(buyOrders);
			
			ret.addObject("indexPageModel", indexPageModel);
			
			sqlSession.close();
		}

		return ret;
	}
	
	@RequestMapping(value="/switch_trading_pair.html", method=RequestMethod.GET)
	public ModelAndView switchTradingPair(
			@RequestParam(value="pairId") Integer pairId) throws Exception {
		
		ModelAndView ret = new ModelAndView(REDIRECT_INDEX);
		
		this.user.setTradingPair(pairId);
		
		return ret;
		
	}
	
}
